﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using Palsta.Core.data;
using Palsta.Core.action;

namespace Palsta.Core.view
{
    class Template
    {
        private DomNode root = null;
        private String htmlText = null;
        private String match = "";

        public Template(DomNode n, String match)
        {
            this.match = match;
            this.root = n;
        }

        public String ToHtml()
        {
            if (this.htmlText != null)
            {
                return this.htmlText;
            }
            this.htmlText = this.GetHtml(this.root);
            return this.htmlText;
        }

        private String GetHtml(DomNode node)
        {
            switch (node.GetNodeType())
            {
                case DomNodeType.General:
                    DomGeneralNode gn = (DomGeneralNode)node;
                    String text = "";
                    for (int i = 0; i < gn.SubNodes.Count; i++)
                    {
                        text += this.GetHtml(((DomNode)gn.SubNodes[i]));
                    }
                    return text;

                case DomNodeType.Condition:
                    return "";

                case DomNodeType.Html:
                    DomHtmlNode hn = (DomHtmlNode)node;

                    StringBuilder sb = new StringBuilder();
                    String domText = null;

                    String bindingName = InfoPathXml.GetNodeAttr(hn.Html, "xd:binding");
                    if (hn.SubNode != null)
                    {
                        domText = this.GetHtml(hn.SubNode);
                    }

                    if (hn.Html.Name.Equals("body"))
                    {
                        InfoPathXml.SetNodeAttr(hn.Html, "onload", "init()");
                    }

                    sb.Append("<").Append(hn.Html.Name);
                    for (int i = 0; i < hn.Html.Attributes.Count; i++)
                    {
                        XmlAttribute attr = hn.Html.Attributes[i];
                        sb.Append(" ").Append(attr.Name.Replace(':', '_')).Append("=\"").Append(attr.Value).Append("\"");
                    }
                    sb.Append(">");

                    if (hn.Html.Name.Equals("head"))
                    {
                        sb.Append("\r\n<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"></meta>");
                        sb.Append("\r\n<script type=\"text/javascript\" src=\"jquery-1.8.2.js\"></script>");
                        sb.Append("\r\n<script type=\"text/javascript\" src=\"xslt-lib.js\"></script>");
                        sb.Append("\r\n<script type=\"text/javascript\" src=\"action.js\"></script>");

                    }

                    if (domText != null)
                    {
                        sb.Append("\r\n").Append(domText).Append("\r\n");
                    }
                    sb.Append("</").Append(hn.Html.Name).Append(">");
                    return sb.ToString();

                case DomNodeType.SetAttribute:
                    return "";

                case DomNodeType.Text:
                    DomTextNode tn = (DomTextNode)node;
                    return tn.Text;

                case DomNodeType.Var:
                    return "";
            }
            return "";

        }


        public JSBlock GenerateJs(ActionMap action, DataDictionary data)
        {
            return this.GenerateJs(action, data, this.root, null);
        }

        private JSBlock GenerateJs(ActionMap action, DataDictionary data, DomNode node, XmlNode contextNode)
        {
            JSBlock js = null;
            JSBlock rst = null;
            String domId;
            switch (node.GetNodeType())
            {
                case DomNodeType.General:
                    DomGeneralNode gn = (DomGeneralNode)node;
                    rst = null;
                    for (int i = 0; i < gn.SubNodes.Count; i++)
                    {
                        DomNode n = (DomNode)gn.SubNodes[i];
                        js = this.GenerateJs(action, data, n, contextNode);

                        if (js.JS.Length > 0)
                        {
                            if (rst == null)
                            {
                                rst = js;
                            }
                            else
                            {
                                rst.JS = rst.JS + "+" + js.JS;
                                rst.AppandReference(js);
                            }
                        }
                    }
                    if (rst == null)
                    {
                        rst = new JSBlock();
                    }
                    return rst;

                case DomNodeType.Condition:
                    DomConditionNode cn = (DomConditionNode)node;

                    if (cn.ConditionID != null)
                    {
                        JSBlock oriConditionJs = (JSBlock)action.ConditionJS[cn.ConditionID];
                        if (oriConditionJs != null)
                        {
                            rst = new JSBlock(cn.ConditionID + "()");
                            rst.AppandReference(oriConditionJs);
                            return rst;
                        }
                    }

                    js = (JSBlock)action.ExpressionJS[cn.Exp.ID];
                    if (js == null)
                    {
                        js = cn.Exp.GenerateExpJs(data);
                        action.SetExpJs(cn.Exp.ID, js);
                    }


                    JSBlock trueJs = null;
                    if (cn.TrueNode != null)
                    {
                        trueJs = this.GenerateJs(action, data, cn.TrueNode, contextNode);
                    }
                    JSBlock falseJs = null;
                    if (cn.FalseNode != null)
                    {
                        falseJs = this.GenerateJs(action, data, cn.FalseNode, contextNode);
                    }

                    StringBuilder evalueJs = new StringBuilder();
                    JSBlock newConditionJs = new JSBlock();
                    evalueJs.Append("    if(" + js.JS + ")\r\n    {\r\n        ");
                    evalueJs.Append("return ").Append(trueJs.JS);
                    newConditionJs.AppandReference(trueJs);

                    evalueJs.Append(";\r\n    }\r\n");
                    evalueJs.Append("    else\r\n    {\r\n        ");

                    if (falseJs != null)
                    {
                        evalueJs.Append("return ").Append(falseJs.JS);
                        newConditionJs.AppandReference(falseJs);
                    }
                    else
                    {
                        evalueJs.Append("return \"\"");
                    }
                    evalueJs.Append(";\r\n    }\r\n");
                    newConditionJs.JS = evalueJs.ToString();
                    newConditionJs.AppandReference(js);

                    String newConditionId = action.GenerateConditionId();
                    action.SetConditionJs(newConditionId, newConditionJs);
                    cn.ConditionID = newConditionId;

                    rst = new JSBlock(cn.ConditionID + "()");
                    rst.AppandReference(newConditionJs);
                    return rst;

                case DomNodeType.Html:
                    DomHtmlNode hn = (DomHtmlNode)node;

                    js = this.GenerateJs(action, data, hn.SubNode, hn.Html);
                    if (js == null)
                    {
                        return null;
                    }
                    js.JS = js.JS.Trim();

                    String htmlFiledName = InfoPathXml.GetNodeAttr(hn.Html, "xd:binding");
                    if ((js.JS.Length > 0 && js.ReferenceFields.Count > 0) || htmlFiledName != null)
                    {
                        domId = InfoPathXml.GetNodeAttr(hn.Html, "domId");
                        if (domId == null)
                        {
                            domId = action.GenerateDomId();
                            InfoPathXml.SetNodeAttr(hn.Html, "domId", domId);
                            InfoPathXml.SetNodeAttr(hn.Html, "onchange", "onChange_" + domId + "()");

                            if (htmlFiledName != null)
                            {
                                action.FieldDomId.Add(htmlFiledName, domId);
                            }
                        }
                        if (js.JS.Length > 0 && js.ReferenceFields.Count > 0)
                        {
                            action.SetEvalueFieldJs(domId, js);
                        }
                    }

                    return new JSBlock();

                case DomNodeType.SetAttribute:
                    DomSetAttributeNode sn = (DomSetAttributeNode)node;
                    domId = InfoPathXml.GetNodeAttr(contextNode, "domId");
                    if (domId == null)
                    {
                        domId = action.GenerateDomId();
                        InfoPathXml.SetNodeAttr(contextNode, "domId", domId);
                        InfoPathXml.SetNodeAttr(contextNode, "onchange", "onChange_" + domId + "()");

                        String domFiledName = InfoPathXml.GetNodeAttr(contextNode, "xd:binding");
                        if (domFiledName != null)
                        {
                            action.FieldDomId.Add(domFiledName, domId);
                        }
                    }
                    String attrDefault = InfoPathXml.GetNodeAttr(contextNode, sn.AttrName);
                    String initAttrExp = "";
                    if (attrDefault == null)
                    {
                        initAttrExp = "removeAttribute(\"" + domId + "\", \"" + sn.AttrName.Replace(':', '_') + "\")";
                    }
                    else
                    {
                        initAttrExp = "setAttribute(\"" + domId + "\", \"" + sn.AttrName.Replace(':', '_') + "\",\"" + attrDefault + "\")";
                    }
                    action.AddAffectAttribute(domId, new JSBlock(initAttrExp));

                    js = this.GenerateJs(action, data, sn.ValueNode, contextNode);
                    if (js == null)
                    {
                        js = new JSBlock();
                        js.JS = "\"\"";
                    }

                    rst =  new JSBlock("setAttribute('" + domId + "', '" + sn.AttrName.Replace(':', '_') + "', " + js.JS + ")");
                    rst.AppandReference(js);
                    return rst;

                case DomNodeType.Text:
                    DomTextNode tn = (DomTextNode)node;
                    rst = new JSBlock();
                    if (tn.Text.Trim().Length > 0)
                    {
                        rst.JS = "\"" + tn.Text.Trim() + "\"";
                    }
                    return rst;

                case DomNodeType.Var:
                    DomVarNode vn = (DomVarNode)node;

                    js = new JSBlock("getField(\"" + vn.VarName + "\")");
                    js.ReferenceFields.Add(vn.VarName);
                    return js;
            }
            return null;

        }
    }
}
